package 多线程;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;


public class Test1 {
    public static void main(String[] args) {
        DecimalAccount.demo(new DecimalAccountCas(new BigDecimal("10000")));
    }
}

class DecimalAccountCas implements DecimalAccount {

    private AtomicReference<BigDecimal> balance;

    public DecimalAccountCas(BigDecimal balance) {//构造函数
        this.balance = new AtomicReference<>(balance);
    }
    
    //实现接口方法：获取balance的值
    @Override
    public BigDecimal getBalance() {
        return balance.get();//AtomicReference类实例方法
    }
    
    //实现接口方法：取款
    @Override
    public void withdraw(BigDecimal amount) {
        while (true) {
            BigDecimal prev = balance.get();//获取原始数据(内存数据)
            BigDecimal next = prev.subtract(amount);//amount为取款数

            if (balance.compareAndSet(prev, next)) {//如果原始数据（内存数据）与prev相等，则原始数据用next替换，并返回true
                break;
            }
            //如果原始数据（内存数据）与prev不相等，则不替换，继续循环，再次获取原始数据(内存数据)进行比较
        }
    }
}

interface DecimalAccount {
    //获取余额
    BigDecimal getBalance();

    //取款
    void withdraw(BigDecimal amount);

    static void demo(DecimalAccountCas account) {//接口定义的静态方法，可直接用：接口名.静态方法名 调用
        List<Thread> ts = new ArrayList<>();
        //定义1000个线程取款，每个线程取款10
        for (int i = 0; i < 1000; i++) {
            ts.add(new Thread(() -> {
                account.withdraw(new BigDecimal(10));//每次从账号中取10
            }));
        }

        long start = System.nanoTime();
        ts.forEach(Thread::start);//相当于：ts.forEach((t)->t.start());
        ts.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        long end = System.nanoTime();
        long time=end-start;
        System.out.println("剩余钱数： " + account.getBalance()+" 耗时："+time);
    }
}

//2.AtomicStampedReference

//import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 通过添加Stamp（版本号），来判断是否被人修改过，从而解决ABA问题
 */
/*public class Test1 {
    static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);

    public static void main(String[] args) throws InterruptedException {
        //获取这个变量A的值和版本号
        String prev = ref.getReference();  //A
        int stamp = ref.getStamp(); //0
        System.out.println("初始版本号： " +stamp );
        other();
        Thread.sleep(1000);
        System.out.println("所有修改完成之后的版本号： " + ref.getStamp()); //2
        //尝试修改为C
        System.out.println("change A -> C : " + ref.compareAndSet(prev, "C", stamp, stamp + 1));
    }

    private static void other() throws InterruptedException {
        new Thread(() -> {
            int stamp = ref.getStamp();
            System.out.println("A -> B之前的版本号： " + stamp);
            System.out.println("change A -> B: " + ref.compareAndSet(ref.getReference(), "B", stamp, stamp + 1));
        },"t1").start();

        Thread.sleep(500);

        new Thread(() -> {
            int stamp = ref.getStamp();
            System.out.println("B -> A之前的版本号： " + stamp);
            System.out.println("change B -> A: " + ref.compareAndSet(ref.getReference(), "A", stamp, stamp +1));
        },"t2").start();
    }
}*/

//3.AtomicMarkableReference


/**
 *AtomicMarkableReference: 
 * 和 AtomicStampedReference 不同之处在于，不关心引用变量更改了几次，只是单纯的关心是否更改过，所以就有了AtomicMarkableReference
 */
/*import java.util.concurrent.atomic.AtomicMarkableReference;
public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        GarbageBag<String> bag = new GarbageBag<>("装满了垃圾");
        //参数 mark 可以看成一个标记，表示垃圾袋满了 初值为：bag=new GarbageBag<>("装满了垃圾")  true
        AtomicMarkableReference<GarbageBag> ref = new AtomicMarkableReference<>(bag, true);
        System.out.println("start...");
        GarbageBag prev = ref.getReference();
        System.out.println(prev.value+" "+ref.isMarked());

        Thread.sleep(1000);

        System.out.println("想更换一只新垃圾袋？");
        boolean success = ref.compareAndSet(prev, new GarbageBag("空垃圾袋"),ref.isMarked(), false);
        System.out.println("换了吗？" + success);
        System.out.println(ref.getReference().value+" "+ref.isMarked());
    }
}

class  GarbageBag<T> {
    T value;

    public GarbageBag(T value) {
        this.value = value;
    }
}*/



